《Android开源库》 Realm For Android~ Adapter &Supporting other library(译文)

Adapter(适配器)

Realm提供了一些抽象的工具类来方便地将 OrderedRealmCollection (RealmResults 和 RealmList 都实现了这个借口)展示到UI控件上。

  • RealmBaseAdapter 可以与 ListView 配合使用。参见示例.
  • RealmRecyclerViewAdapter 可以与 RecyclerView 配合使用。参见示例. 你需要在 app 的 build.gradle 中添加额外的依赖以使用这些适配器。
1
2
3
dependencies {
compile 'io.realm:android-adapters:1.4.0'
}

Intent

因为你不可以直接通过 intent 传递 RealmObject,我们建议你只传递 RealmObject 的标识符。举个常用例子,假如你的对象拥有一个主键,请通过 intent 的 bundle 来传递这个主键的值。

1
2
3
4
// Assuming we had a person class with a @PrimaryKey on the 'id' field ...
Intent intent = new Intent(getActivity(), ReceivingService.class);
intent.putExtra("person_id", person.getId());
getActivity().startService(intent);

在接受方(Activty、Service、IntentService、BroadcastReceiver 及其它)从 bundle 中解析出这个主键然后打开 Realm 查询得到这个 RealmObject。

1
2
3
4
5
6
// in onCreate(), onHandleIntent(), etc.
String personId = intent.getStringExtra("person_id");
Realm realm = Realm.getDefaultInstance();
Person person = realm.where(Person.class).equalTo("id", personId).findFirst();
// do something with the person ...
realm.close();

可以参考 threading example 中的 Object Passing 部分。该示例展示了在安卓开发中常用的如何传递 id 并且得到对应的 RealmObject。

Android Framework 多线程 API 相关

当你使用下列 API 时请小心:

  • AsyncTask
  • IntentService

AsyncTask 的 doInBackground() 方法会运行在一个后台线程。IntentService 的 onHandleIntent(Intent intent) 方法会运行在一个后台工作线程。

如果你需要在这些方法中使用 Realm,请在对 Realm 的调用结束后关闭 Realm 实例。见如下例子。

AsyncTask

在 doInBackground 方法中打开并关闭 Realm,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private class DownloadOrders extends AsyncTask<Void, Void, Long> {
protected Long doInBackground(Void... voids) {
// Now in a background thread.

// Open the Realm
Realm realm = Realm.getDefaultInstance();
try {
// Work with Realm
realm.createAllFromJson(Order.class, api.getNewOrders());
Order firstOrder = realm.where(Order.class).findFirst();
long orderId = firstOrder.getId(); // Id of order
return orderId;
} finally {
realm.close();
}
}

protected void onPostExecute(Long orderId) {
// Back on the Android mainThread
// do something with orderId such as query Realm
// for the order and perform some operation with it.
}
}
IntentService

ChangeListener 在 IntentService 中不会工作。尽管 IntentService 本身是一个 Looper 线程,但每次 onHandleIntent 的调用是独立的事件。你可以注册监听器的调用不会返回失败,但他们永远不会被触发。

在 onHandleIntent() 方法中打开并关闭 Realm,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class OrdersIntentService extends IntentService {
public OrdersIntentService(String name) {
super("OrdersIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {
// Now in a background thread.

// Open the Realm
Realm realm = Realm.getDefaultInstance();
// Work with Realm
realm.createAllFromJson(Order.class, api.getNewOrders());
Order firstOrder = realm.where(Order.class).findFirst();
long orderId = firstOrder.getId(); // Id of order
realm.close();
}
}

对其它库的支持

这个章节描述了怎样与其它 Android 流行库搭配使用 Realm。

GSON

GSON 是 Google 开发的 JSON 处理库。GSON 与 Realm 可以无缝配合使用。

1
2
3
4
5
6
7
8
9
10
// Using the User class
public class User extends RealmObject {
private String name;
private String email;
// getters and setters left out ...
}

Gson gson = new GsonBuilder().create();
String json = "{ name : 'John', email : 'john@corporation.com' }";
User user = gson.fromJson(json, User.class);

GridViewExample 展示了如何配合 GSON 使用 Realm。

序列化(Serialization)

你有时需要序列化与反序列化一个 Realm 对象以便与其它库(比如 Retrofit)相配合。因为 GSON使用成员变量值而非 getter 和 setter ,所以你无法通过 GSON 的一般方法来序列化 Realm 对象。

你需要为 Realm 模型对象自定义一个 JsonSerializer 并且将其注册为一个 TypeAdapter

请参考这个 Gist

数组(Primitive lists)

某些 JSON API 会以数组的形式返回原始数据类型(例如 String 和 integer),Realm 暂时不支持对这种数组的处理。但你可以通过自定义 TypeAdapter来处理这种情况。

这个 Gist 展示了如何将 JSON 中的整型数组存入 Realm。类似地,你可以用这个方法来处理其它原始数据类型数组。

Troubleshooting

Realm 对象属性可能会包含循环引用。在这种情况下,GSON 会抛出 StackOverflowError。例如如下 Realm 对象拥有一个 Drawable 属性:

1
2
3
4
5
public class Person extends RealmObject {
@Ignore
Drawable avatar;
// other fields, etc
}

Person 类含有一个 Android Drawable 并且被 @Ignore 修饰。当 GSON 序列化时,Drawable 被读取并且造成了堆栈溢出。(GitHub Issue)。添加如下代码以避免类似问题:

1
2
3
public boolean shouldSkipField(FieldAttributes f) {
return f.getDeclaringClass().equals(RealmObject.class) || f.getDeclaringClass().equals(Drawable.class);
}

请注意对 Drawable.class 的判定语句,它告诉 GSON 跳过这个属性的序列化以避免堆栈溢出错误。

Jackson-databind

Jackson-databind 用来绑定 JSON 数据和其对应的 Java 类。

Jackson 需要使用反射进行工作。这与 Realm 的 RxJava 支持有冲突,因为作为可选功能,RxJava 有可能不再工程依赖库中。这会导致类似如下的异常抛出:

1
2
java.lang.NoClassDefFoundError: rx.Observable
at libcore.reflect.InternalNames.getClass(InternalNames.java:55)

为了修正这个问题你可以添加 RxJava 依赖到工程中或者创建一个假类文件如下所示:

package rx;

public class Observable {
// Dummy class required for Jackson-Databind support if
// RxJava is not a project dependency.
} 这个问题也同时报告给了 Jackson 项目。

Kotlin

Realm 完全兼容 Kotlin 语言,但有些地方需要注意:

Parceler

Parceler 可以帮助对象自动生成支持 Parcelable 接口的样板代码。因为 Realm 的代理类,你需要以下设置以便应用 Parceler 到 Realm 对象。

1
2
3
4
5
6
7
8
9
10

// All classes that extend RealmObject will have a matching RealmProxy class created
// by the annotation processor. Parceler must be made aware of this class. Note that
// the class is not available until the project has been compiled at least once.
@Parcel(implementations = { PersonRealmProxy.class },
value = Parcel.Serialization.BEAN,
analyze = { Person.class })
public class Person extends RealmObject {
// ...
}

如果你使用 Gradle 来获取 Parceler,请确保存在以下配置代码:

1
2
compile "org.parceler:parceler-api:1.0.3"
apt "org.parceler:parceler:1.0.3"

请注意目前在使用 Parceler 的时候有如下的限制:

  • 如果你的模型包含 RealmList,那么你需要注册一个特殊 adapter
  • 一旦对象被打包(parcelled),它将变为一个有当前数据快照,不再被 Realm 管理的一个 unmanaged 对象。之后该对象的数据变化不会被 Realm 写入。

Retrofit

Retrofit是一个由 Square 开发,保证类型安全(typesafe)的 REST API 处理工具。

Realm 可以与 Retrofit 1.x 和 2.x 无缝配合工作。但请注意 Retrofit 不会自动将对象存入 Realm。你需要通过调用 Realm.copyToRealm() 或 Realm.copyToRealmOrUpdate() 来将它们存入 Realm。

1
2
3
4
5
6
7
GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");

// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();

Robolectric

通过 Robolectric库可以让你在不使用真实设备或者模拟器的情况下直接在 Java 虚拟机上进行 JUnit 测试。但目前 Roboletrics 不支持带有原生库的测试。而 Realm 包含使用 C++ 的原生库,所以你目前不可以通过 Roboletrics 测试使用 Realm 的项目。

你可以关注这个 Robolectric 的新功能请求

RxJava

RxJava 是 Netflix 发布的一个 [Reactive 的扩展 库以支持 观察者模式

Realm 包含了对 RxJava 的原生支持。如下类可以被暴露为一个 Observable:Realm, RealmResults, RealmObject, DynamicRealm and DynamicRealmObject。

1
2
3
4
5
6
7
8
9
10
// Combining Realm, Retrofit and RxJava (Using Retrolambda syntax for brevity)
// Load all persons and merge them with their latest stats from GitHub (if they have any)
Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
realm.where(Person.class).isNotNull("username").findAllAsync().asObservable()
.filter(persons.isLoaded)
.flatMap(persons -> Observable.from(persons))
.flatMap(person -> api.user(person.getGithubUserName())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> showUser(user));

请注意异步查询不会阻塞当前线程,如上代码会立即返回一个 RealmResults 实例。如果你想确定该 RealmResults 已经加载完成请使用filter operator 和 RealmResults<E>.isLoaded() 方法。通过判断 RealmResults 是否已经加载可以得知查询是否已经完成。

参考 RxJava sample project

配置

RxJava 是可选依赖,这意味着 Realm 不会自动包含它。这样做的好处是你可以选择需要的 RxJava 版本以及防止过多的无用方法被打包。如果你要使用相关功能,请手动添加 RxJava 到 build.gradle 文件。

1
2
3
dependencies {
compile 'io.reactivex:rxjava:1.1.0'
}

你也可以通过继承 RxObservableFactory 来决定 Observable 的生成方式,然后通过 RealmConfiguration 进行配置。

1
2
3
RealmConfiguration config = new RealmConfiguration.Builder()
.rxFactory(new MyRxFactory())
.build()

如果没有 RxObservableFactory 被定义,RealmObservableFactory 会被默认使用,它支持 RxJava <= 1.1.*。

原文链接

https://realm.io/cn/docs/java/latest/

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×